package com.atguigu.controller;

import com.atguigu.service.OrderService;
import com.atguigu.service.UserService;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("stock")
@Slf4j
public class StockController {

    @Autowired
    private OrderService orderService;

    //创建令牌桶实例
    private RateLimiter rateLimiter = RateLimiter.create(40);// 40个令牌

    @Autowired
    private UserService userService;

    //创建令牌桶实例
//    private RateLimiter rateLimiter = RateLimiter.create(40);
//    private static int cnt=1;

//    @GetMapping("sale")
//    public String sale(String id){
//        // 1.没有获取到token请求，会一直等，直到获取到token令牌
//        //log.debug("等待的时间："+ rateLimiter.acquire());
//        // 2.设置一个等待时间，如果在规定时间内获取到了token令牌，则处理业务，如果在规定时间内没有获取到，则抛弃请求
//        if (!rateLimiter.tryAcquire(2, TimeUnit.SECONDS)) { //要求2秒内拿到，不然抛弃
//            System.out.println("当前请求被限流了，直接抛弃");
//            return "抢购失败";
//        }
//        System.out.println("处理业务。。。。。。"+cnt++);
//        return "success";
//  }

    // 单用户限次方位
    @GetMapping("/killTokenAndMd5AndLimit")
    public String killTokenAndMd5AndLimit(String userId, String stockId,String Md5) {

        if (!rateLimiter.tryAcquire(10, TimeUnit.SECONDS)) {
            // 被限流，抛弃请求了
            log.info("被限流，抛弃请求了");
            return "秒杀失败，当前秒杀活动十分火爆，请重试！";
        }
        String id;
        try {
            // 加入单用户限制次数
            int count = userService.setLimitCount(userId);
            log.info("用户当前访问的次数为 [{}]",count);
            // 看看该用户是否已经超过次数，禁止访问
            boolean isBanned = userService.getLimitCount(userId);
            if(isBanned){
                log.error("购买失败，超过购买频率限制~~！");
                return "购买失败，超过购买频率限制！";
            }

            id = orderService.createOrderAfterMd5Limit(userId,stockId,Md5);
            return "秒杀成功，订单id = " + id;
        }catch (Exception e){
            e.printStackTrace();
            return e.getMessage();
        }
    }




    // 隐藏接口，防止黑客脚本
    @GetMapping("/md5")
    public String getMd5(String userId,String stockId){
        String md5 = orderService.getMd5(userId, stockId);
        return md5;
    }

    @GetMapping("/killTokenAndMd5")
    public String killTokenAndMd5(String userId, String stockId,String Md5) {

        if (!rateLimiter.tryAcquire(2, TimeUnit.SECONDS)) {
            // 被限流，抛弃请求了
            log.info("被限流，抛弃请求了");
            return "秒杀失败，当前秒杀活动十分火爆，请重试！";
        }
        String id;
        try {
            id = orderService.createOrderAfterMd5(userId,stockId,Md5);
            return "秒杀成功，订单id = " + id;
        }catch (Exception e){
            e.printStackTrace();
            return e.getMessage();
        }
    }




    // 开发一个秒杀方法，乐观锁防止超卖 + 令牌桶实现限流
    // 限流之后，不一定卖得完
    @GetMapping("/killToken/{sid}")
    public String killToken(@PathVariable("sid") String sid) {

        if (!rateLimiter.tryAcquire(2, TimeUnit.SECONDS)) {
            // 被限流，抛弃请求了
            log.info("被限流，抛弃请求了");
            return "秒杀失败，当前秒杀活动十分火爆，请重试！";
        }
        String id;
        try {
            id = orderService.createOrder(sid);
            return "秒杀成功，订单id = " + id;
        }catch (Exception e){
            e.printStackTrace();
            return e.getMessage();
        }
    }


    //使用乐观锁防止超卖
    @GetMapping("/kill/{sid}")
    public String kill(@PathVariable("sid") String sid) {
        String id;
        try {
            // 悲观锁解决，要锁在conroller层，因为service 有事务
            /*synchronized (this){
                id = orderService.createOrder(sid);
                return "秒杀成功，订单id = " + id;
            }*/
            // 乐观锁解决商品超卖问题，实际上是把主要防止超卖问题交给数据库解决，利用数据库中定义的version字段
            // 以及数据库中的事务实现了超卖问题
            /*
                //在更新库存时把版本号也修改（在sql层面）
            * update sale=sale+1,version=version+1  where id=#{id} and version=#{stock.version}
            * if result=0   ---> 抢购失败
            *
            * */
            id = orderService.createOrder(sid);
            return "秒杀成功，订单id = " + id;
        }catch (Exception e){
            e.printStackTrace();
            return e.getMessage();
        }
    }
}
